package ${projectDomain}.filter;

import java.nio.charset.StandardCharsets;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import ${projectDomain}.config.RequestConfig;
import ${projectDomain}.pojo.vo.ResponseBody;
import com.unswift.exception.CoreException;
import com.unswift.utils.EncryptionUtils;
import com.unswift.utils.ExceptionUtils;
import com.unswift.utils.JsonUtils;
import com.unswift.utils.ObjectUtils;

import io.netty.buffer.ByteBufAllocator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Component
@Api(value="签名拦截器，此拦截器应当紧随缓存body拦截器之后", author = "unswift", date = "2023-08-13", version = "1.0.0")
public class SignFilter extends CloudFilter implements GlobalFilter, Ordered{

	@Autowired
	@ApiField("请求yml配置")
	private RequestConfig requestConfig;
	
	@Override
	@ApiMethod(value="获取filter的执行顺序，值越小，越靠前，此值最好不要大于1，否则的话取到的path会没有路由", returns = @ApiField("执行顺序"))
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE+1;
	}
	
	@Override
	@ApiMethod(value="拦截器执行方法，在此方法中拦截签名", params = {@ApiField("http请求的封装，提供对HTTP的访问请求和响应"), @ApiField("委托")})
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		try {
			ServerHttpRequest request = exchange.getRequest();
			String path=request.getPath().value();
			if(requestConfig.matchSignWhite(path)) {
				return chain.filter(exchange);
			}
			logger.error("sign："+path);
			HttpHeaders headers = request.getHeaders();
			String website=headers.getFirst("Website");
			ExceptionUtils.empty(website, "invalid.request");
			String language=headers.getFirst("Language");
			ExceptionUtils.empty(language, "invalid.request");
			String version=headers.getFirst("Version");
			ExceptionUtils.empty(version, "invalid.request");
			String signTime=headers.getFirst("Datetime");
			ExceptionUtils.empty(signTime, "invalid.request");
			if(System.currentTimeMillis()-Long.parseLong(signTime)>requestConfig.getSignTimeout()) {
				logger.error("请求超时："+path);
				ExceptionUtils.message("invalid.request");
			}
			StringBuilder signData=new StringBuilder(path);
			if(!request.getQueryParams().isEmpty()) {
				int[] firstParam= {signData.indexOf("?"), 0};
				request.getQueryParams().forEach((k, v)->{
					signData.append(firstParam[0]==-1 && firstParam[1]==0?"?":"&");
					if(ObjectUtils.isNotEmpty(v) && v.size()==1) {
						signData.append(String.format("%s=%s", k, v.get(0)));
					}else if(ObjectUtils.isNotEmpty(v) && v.size()>1) {
						signData.append(String.format("%s=%s", k, JsonUtils.toJson(v)));
					}else {
						signData.append(String.format("%s=%s", k, ""));
					}
					firstParam[1]++;
				});
			}
			String body=this.getRequstBody(request);
	    	if(ObjectUtils.isNotEmpty(body)){
	    		signData.append(body);
	    	}
	    	signData.append(signTime);
	    	signData.append(website);
	    	signData.append(language);
	    	signData.append(version);
			String token=headers.getFirst(tokenConfig.getKeyName());
			if(ObjectUtils.isNotEmpty(token)) {
				token=this.getUserToken(token);
			}
	    	signData.append(ObjectUtils.init(token, requestConfig.getSignDefaultKey()));
	    	
	    	String matchSignData=headers.getFirst("Sign");
			ExceptionUtils.empty(matchSignData, "invalid.request");
			if(!EncryptionUtils.md5Validate(signData.toString(), matchSignData)) {
				logger.error("需要签名的数据："+signData.toString());
				throw ExceptionUtils.message("illegal.request");
			}
	    	
			byte[] bytes=ObjectUtils.init(body, "").getBytes(StandardCharsets.UTF_8);
			NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
			DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
			buffer.write(bytes);
			Flux<DataBuffer> bodyFlux = Flux.just(buffer);
			ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
				@Override
				public Flux<DataBuffer> getBody() {
					return bodyFlux;
				}
			};
			return chain.filter(exchange.mutate().request(mutatedRequest).build());
		} catch (CoreException e) {
			e.printStackTrace();
			return toJson(exchange, ResponseBody.error(e.getCode(), e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage(), e);
			return toJson(exchange, ResponseBody.error("unknown.error", String.format(ExceptionUtils.getMessage("unknown.error"), e.getMessage())));
		}
	}
	
}
